package extensionTools.smvCreator;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Dictionary;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;

import extensionTools.smvCreator.SmvProcess;
import extensionTools.smvCreator.Link.LinkType;
import extensionTools.smvCreator.Link.SmvType;
import extensionTools.smvCreator.State.stateType;
import extensionTools.smvCreator.Exceptions.SmvDBException;

import exportedAPI.OpcatConstants;
import exportedAPI.opcatAPI.IEntry;
import exportedAPI.opcatAPI.ILinkEntry;
import exportedAPI.opcatAPI.ILinkInstance;
import exportedAPI.opcatAPI.IObjectInstance;
import exportedAPI.opcatAPI.IOpd;
import exportedAPI.opcatAPI.IProcessInstance;
import exportedAPI.opcatAPI.IRelationEntry;
import exportedAPI.opcatAPI.IRelationInstance;
import exportedAPI.opcatAPI.IStateEntry;
import exportedAPI.opcatAPI.IStateInstance;
import exportedAPI.opcatAPI.ISystem;
import gui.projectStructure.ObjectEntry;
import gui.projectStructure.ProcessEntry;
import gui.projectStructure.StateEntry;

import java.lang.Math;

/**
* This class holds all the data from the OPM diagram which is required in order to build the output SMV script.
* @author Oshrit & Lital
*/
public class DiagramDatabase 
{
        /* PRIVATE MEMBERS */
        
        /**
         * List of all the objects in the diagram except instance's objects.
         */
        private ArrayList<SmvObject> smvObjectList;
        
        /**
         * List of all the instance's objects in the diagram.
         */
        private ArrayList<SmvInstanceObject> smvInstanceObjectList;
        
        /**
         * List of all the processes in the diagram
         */
        private ArrayList<SmvProcess> smvProcessList;   
        
        /**
         * List of all main processes (in-zooming processes) - for use in class only!
         */
        private ArrayList<IProcessInstance> mainProcessList;
        
        /**
         * List of all states
         */
        private ArrayList<State> statesList;
        
        /**
         * List of process that makes updates to objects
         */
        private ArrayList<SmvProcess> updateProcessList;
        
        /**
         * Dictionary: key - SmvProcess ID, value - SmvProcess father ID
         */
        private Dictionary<Long, Long> fatherDictionary;
        
        private int sysTerm;
        private int evTerm;
        private int creationVar;
        /* C'TOR */
        
        /**
         * Class constructor
         * @param currSystem The current system
         * @param sysTerm The initial value for sysTerminate
         * @param evTerm The initial value for evTerminate
         * @param creation_var 
         * @throws IOException
         */
        public DiagramDatabase(ISystem currSystem, int sysTerm, int evTerm, int creation_var) throws IOException 
        {
            smvObjectList = new ArrayList<SmvObject>();
            setSmvInstanceObjectList(new ArrayList<SmvInstanceObject>());
            smvProcessList = new ArrayList<SmvProcess>();   
            mainProcessList = new ArrayList<IProcessInstance>();
            statesList = new ArrayList<State>();            
            updateProcessList = new ArrayList<SmvProcess>();
            fatherDictionary = new Hashtable<Long, Long>(); 
            
            //initialize the parameters for evTerminate and sysTerminate and object creation
            this.evTerm = evTerm;
            this.sysTerm = sysTerm;
            this.setCreationVar(creation_var);
            
            Enumeration<?> opds = currSystem.getISystemStructure().getOpds();       
            // Create the list of the main processes in the system
            while (opds!=null && opds.hasMoreElements())
            {
                IOpd currOpd= (IOpd)opds.nextElement();
                if ( (currOpd.getMainIInstance() != null) && (currOpd.getMainIInstance() instanceof IProcessInstance) )
                {
                        mainProcessList.add((IProcessInstance)currOpd.getMainIInstance());
                }
            }
            
        }
        
        /* PROPERTIES */
        
        /**
         * @return The list of objects in the diagram
         */
        public ArrayList<SmvObject> getSmvObjectList() {
                return smvObjectList;
        }

        /**
         * 
         * @return The list of instance objects in the diagram
         */
        public ArrayList<SmvInstanceObject> getSmvInstanceObjectList() {
                return smvInstanceObjectList;
        }
                
        /**
         * @return The list of processes in the diagram
         */
        public ArrayList<SmvProcess> getSmvProcessList() {
                return smvProcessList;
        }
        
        /**
         * @param sysTerm the sysTerm to set
         */
        public void setSysTerm(int sysTerm) {
                this.sysTerm = sysTerm;
        }

        /**
         * @return the sysTerm
         */
        public int getSysTerm() {
                return sysTerm;
        }

        /**
         * @param evTerm the evTerm to set
         */
        public void setEvTerm(int evTerm) {
                this.evTerm = evTerm;
        }

        /**
         * @return the evTerm
         */
        public int getEvTerm() {
                return evTerm;
        }
       
        /**
         * @return the stateList
         */
        public ArrayList<State> getSmvStateList() {      
                return statesList;
        }
        
        /**
		 * @param creationVar the creationVar to set
		 */
		public void setCreationVar(int creationVar) {
			this.creationVar = creationVar;
		}

		/**
		 * @return the creationVar
		 */
		public int getCreationVar() {
			return creationVar;
		}

		/**
		 * 
		 * @param updateProcessList
		 */
		public void setUpdateProcessList(ArrayList<SmvProcess> updateProcessList) {
			this.updateProcessList = updateProcessList;
		}

		/**
		 * 
		 * @return updateProcessList
		 */
		public ArrayList<SmvProcess> getUpdateProcessList() {
			return updateProcessList;
		}
        
        /* PUBLIC METHODS */

        /**
         * This is the main function of this class
         * It runs over all the OPDs in the diagram, reads all elements,
         * and separates them into our sets according to their type
         * @param currSystem The current ISystem
         * @throws IOException
         * @throws SmvDBException 
         */
        public void acquireDiagramDetails(ISystem currSystem) throws SmvDBException
        { 
            try
            {
                    //running over all the Opds and finding all processes and objects in the system
                    Enumeration<?> opds = currSystem.getISystemStructure().getOpds();       
                    while (opds!=null && opds.hasMoreElements())
                    {
                            IOpd currOpd= (IOpd)opds.nextElement();
                            long currOpdId = currOpd.getOpdId();
                            
                            createObjectsFromCurrSD(currSystem, currOpdId);
                            
                            //Here we find the main process in the currOpd and set its fields, all links directly 
                            //connected to it and leaf children and their links.
                            setMainProcess(currSystem, currOpd);
                    }
            
                    addRelationsToOpmElements(currSystem);  
                    setFatherToLeafsForProcesses();
                    setSmvInstanceObjectList();
                    setLableList();
                    
            }
            catch(Exception e)
            {
                throw new SmvDBException("DiagramDatabase.acquireDiagramDetails: "+e.getMessage());
            }
            
        }
        
        public boolean compareLink(Link uLink, Link link)
        {
    		if(uLink.getSrcId() == link.getSrcId() && 
			   uLink.getDstId() == link.getDstId() &&
			   uLink.getSrcType() == link.getSrcType() &&
			   uLink.getDstType() == link.getDstType() &&
			   uLink.getLinkType() == link.getLinkType() &&
			   uLink.getLabel().equals(link.getLabel()))
    		{
    			return true;
    		}
        	return false;
        }
        
        /**
         * @param id
         * @return The index of the given object id in the object's arrayList.<br/>
         * -1 is returned if the object doesn't exist in the arrayList.
         */
        public int findObjectById(long id)
        {
            Iterator<SmvObject> i = smvObjectList.iterator();
            while(i.hasNext())
            {
                    SmvObject currObject = (SmvObject)i.next();
                    if(currObject.getId() == id)
                    {
                            return smvObjectList.indexOf(currObject); 
                    }
            } 
            
            return -1;
        }
        
        /**
         * @param id
         * @return The index of the given object id in the object's arrayList.<br/>
         * -1 is returned if the object doesn't exist in the arrayList.
         */
        public int findInstanceObjectById(long id)
        {
            Iterator<SmvInstanceObject> i = smvInstanceObjectList.iterator();
            while(i.hasNext())
            {
                    SmvInstanceObject currObject = (SmvInstanceObject)i.next();
                    if(currObject.getId() == id)
                    {
                            return smvInstanceObjectList.indexOf(currObject); 
                    }
            } 
            
            return -1;
        }
        
        /**
         * @param id
         * @return The index of the given mainProcess id in the mainProcesses arrayList.<br/>
         * -1 is returned if the process doesn't exist in the arrayList (meaning: the process is a leaf)
         */
        public int findMainProcessById(long id)
        {
            Iterator<IProcessInstance> i = mainProcessList.iterator();
            while(i.hasNext())
            {
                    IProcessInstance currMainProcess = (IProcessInstance)i.next();
                    if(currMainProcess.getIEntry().getId() == id)
                    {
                            return mainProcessList.indexOf(currMainProcess); 
                    }
            } 
            
            return -1;
        }
        
        
        /**
         * @param id
         * @return The index of the given process id in the processes arrayList.<br/>
         * -1 is returned if the process doesn't exist in the arrayList.
         */
        public int findProcessById(long id)
        {
            Iterator<SmvProcess> i = smvProcessList.iterator();
            while(i.hasNext())
            {
                    SmvProcess currProcess = (SmvProcess)i.next();
                    if(currProcess.getId() == id)
                    {
                            return smvProcessList.indexOf(currProcess); 
                    }
            } 
            
            return -1;
        }
        
        /** Find process name by his id
         * @param id
         * @return The name of the given process id in the processes arrayList.<br/>
         * null is returned if the process doesn't exist in the arrayList.
         */
        public String getProcessNameById(long id)
        {
            Iterator<SmvProcess> i = smvProcessList.iterator();
            while(i.hasNext())
            {
                    SmvProcess currProcess = (SmvProcess)i.next();
                    if(currProcess.getId() == id)
                    {
                            return currProcess.getName(); 
                    }
            } 
            
            return null;
        }
        /**
         * @param id
         * @return The index of the given process id in the states arrayList.
         * -1 is returned if the process doesn't exist in the arrayList.
         */
        public int findStateById(long id)
        {
            Iterator<State> i = statesList.iterator();
            while(i.hasNext())
            {
                    State currState = (State)i.next();
                    if(currState.getId() == id)
                    {
                            return statesList.indexOf(currState); 
                    }
            } 
            return -1;
        }
        
        /**
         * @param process an SMV process
         * @return The List of the children processes of the given SmvProcess process
         */
        public ArrayList<SmvProcess> getChildrenList(SmvProcess process)
        {
            ArrayList<SmvProcess> childrenList = new ArrayList<SmvProcess>();
            for(int i=0; i<process.getChildrenIdList().size(); i++)
            {
                    childrenList.add(smvProcessList.get(findProcessById(process.getChildrenIdList().get(i))));
                    
            }
            return childrenList;
        }
        
        public boolean isFatherToLeaf(long processId)
        {
        	return smvProcessList.get(findProcessById(processId)).getFatherToLeafs();
        }
        
        /* PRIVATE METHODS */
        
        /**
         * This function creates SmvObject from the given currOpdId and adds it to the SmvObjectList
         * @param currSystem The current ISystem
         * @param currOpdId The current OPD id
         */
        private void createObjectsFromCurrSD(ISystem currSystem, long currOpdId) 
        {
                
            // Here we get an enumeration of Objects in the Opd. 
            Enumeration<?> systemElements = currSystem.getISystemStructure().getInstancesInOpd(currOpdId);
            // Sorting the elements into our sets
            while(systemElements != null && systemElements.hasMoreElements())
            {
               Object obj = systemElements.nextElement();
               if (obj instanceof IObjectInstance)
               { 
                       IObjectInstance myobj = (IObjectInstance)obj; 
                       SmvObject currObject = new SmvObject(myobj.getIEntry().getId(), myobj.getIEntry().getName(), myobj.getIEntry().isEnvironmental());    

                       if(findObjectById(currObject.getId()) == -1)
                       {
                               smvObjectList.add(currObject);
                               setStates(myobj, currObject);
                       }
                       continue;
               }
            }
        }
        
        /**
         * This function finds the main process in the current SD and sets the process details,<br/>
         * checks the children processes and insert them into the children list if they are not the main process
         * @param currSystem the current ISystem
         * @param currOpd The current OPD
         * @throws IOException
         * @throws SmvDBException 
         */
        private void setMainProcess(ISystem currSystem, IOpd currOpd) throws IOException, SmvDBException
        {
                long currOpdId = currOpd.getOpdId();
                SmvProcess process;
                
                if(currOpd.getMainIInstance() == null) //first SD
                {               
                    Enumeration<?> systemElements = currSystem.getISystemStructure().getInstancesInOpd(currOpdId);
                        
                    while(systemElements != null && systemElements.hasMoreElements())
                    {
                       Object obj = systemElements.nextElement();
                       if (obj instanceof IProcessInstance)
                       { 
                           IProcessInstance processInstance = (IProcessInstance)obj;
                           if(findMainProcessById(processInstance.getIEntry().getId()) == -1)
                           {
                                    process = new SmvProcess(processInstance.getIEntry().getId(),
                                                             processInstance.getIEntry().getName(),-1,
                                                             processInstance.getY(), processInstance.getHeight());                                   
                                    smvProcessList.add(process);
                                    createProcess(process, processInstance);
                                    
                           }
                       }
                   }
                }
                
                //for inZooming of process
                if (currOpd.getMainIInstance()!=null && (currOpd.getMainIInstance() instanceof IProcessInstance))
                {
                        IProcessInstance mainProcessInSD = (IProcessInstance)currOpd.getMainIInstance();
                        
                        long mainProcessInSDId = mainProcessInSD.getIEntry().getId();
                        int index = findProcessById(mainProcessInSDId);
                        if(index == -1)
                        {
                                long fatherId = -1;
                                if (fatherDictionary.equals(mainProcessInSDId))
                                {
                                        fatherId = fatherDictionary.get(mainProcessInSDId);
                                }
                                process = new SmvProcess(mainProcessInSDId,
                                                         mainProcessInSD.getIEntry().getName(),fatherId,
                                                         -1, -1);
                        }
                        else
                        {
                            process = smvProcessList.get(index);
                        }
                        
                        smvProcessList.add(process);
                        createProcess(process, mainProcessInSD);
                }        
        }
        
        /**
         * This function sets the main process details, finds all the children of the process,
         * sets their details and sets a linkList.
         * @param mainProcess The main SMV process
         * @param mainProcessInstance The main process instance 
         * @throws IOException
         * @throws SmvDBException 
         */
        private void createProcess(SmvProcess mainProcess, IProcessInstance mainProcessInstance) throws IOException, SmvDBException
        {

                Enumeration<?> children = mainProcessInstance.getChildThings();
                while (children!=null && children.hasMoreElements())
                {               
                		//DEBUG
            			//String className = children.nextElement().getClass().getName();
            			//if( className != "exportedAPI.opcatAPI.IProcessInstance") continue;
            		    
                	
                		IProcessInstance childInstance = (IProcessInstance)(children.nextElement());
                                
                        long childId = childInstance.getIEntry().getId();
                        //add the children id to the process childrenList
                        mainProcess.getChildrenIdList().add(childId);
                        fatherDictionary.put(childId, mainProcess.getId());
                        
                        //only if the current child isn't in-zooming (leaf), 
                        //we create it, because if it is in-zooming process 
                        //it will be mainProcess in some other SD.              
                        if(findMainProcessById(childInstance.getIEntry().getId()) == -1)
                        {       
                                SmvProcess childProcess = new SmvProcess(childId, childInstance.getIEntry().getName(), 
                                                                         mainProcess.getId(), childInstance.getY(), 
                                                                         childInstance.getHeight());
                                
                                smvProcessList.add(childProcess);
                                addLinksToOpmElements(childProcess, childInstance); 
                                
                        } 
                        //update fathreId
                        else
                        {
                        	 int index = findProcessById(childInstance.getIEntry().getId());
                             //DEBUG
                             if(index != -1){
		                        	smvProcessList.get(index).setFatherId(mainProcess.getId());
		                        	smvProcessList.get(index).setHeight(childInstance.getHeight());
		                        	smvProcessList.get(index).setY(childInstance.getY());
                             }                       
                        }
                }               
                
                setTimeLineForProcess(mainProcess);
                addLinksToOpmElements(mainProcess, mainProcessInstance);
                
        }
        
        /**
         * This function sets all links that connect the given process with objects\states<br/>
         * and adds them to its linkList.
         * @param process
         * @param processInstance
         * @throws IOException
         * @throws SmvDBException 
         */
        private void addLinksToOpmElements(SmvProcess process, IProcessInstance processInstance) throws IOException, SmvDBException
        {
                Enumeration<?> links = processInstance.getRelatedInstances();
                while (links!=null && links.hasMoreElements())
                {
                        Object linkInstance = links.nextElement(); 
                                
                        Link link = createLink(linkInstance, SmvType.LINK);
                                                                
                        if(link.getSrcId() == process.getId())
                        {
                            
                            Enumeration<?> xorLinkList = ((ILinkInstance)linkInstance).getOrXorSourceNeighbours(true);
                            Enumeration<?> orLinkList = ((ILinkInstance)linkInstance).getOrXorSourceNeighbours(false);            
                            
                            ArrayList<Link> xorList = addXorOrList(process.getXorLists(), link, xorLinkList);                         
                            if(xorList!=null)
                            {
                                process.getXorLists().add(xorList);
                            }
                            
                            ArrayList<Link> orList = addXorOrList(process.getOrLists(), link, orLinkList);
                            if(orList!=null)
                            {
                                process.getOrLists().add(orList);       
                            }
                            
                            //In case the link to add is not Or/Xor link - we add it to both process and object link list
                            if ((xorLinkList == null) && (orLinkList == null))
                            {
                                addLinkByEdge(process, link, link.getDstId(), link.getDstType(), false);
                            }  
                            ///In case the link to add is Or/Xor link - we add it only to the object link list
                            else
                            {
                                addLinkByEdge(process, link, link.getDstId(), link.getDstType(), true);
                            }
                        }
                        else //link.getDstId() == process.getId(), and not Or/Xor link
                        {
                            addLinkByEdge(process, link, link.getSrcId(), link.getSrcType(), false);
                        }   
                }
        }
        /**
         * This function add xor/or list to the process arrayList of lists.
         * @param process
         * @param link
         * @param xorOrLinkList
         * @param xorOrLists
         */
        private ArrayList<Link> addXorOrList(ArrayList<ArrayList<Link>> processLists, Link link, Enumeration<?> xorOrLinkList) 
        {
                ArrayList<Link> myOrXorList;
                if(xorOrLinkList!=null)
                {
                        myOrXorList = setOrXorRelatedLinks(link, xorOrLinkList, true);
                        myOrXorList.add(link);
                        if(isXorOrListExist(myOrXorList, processLists) == false)
                        {                          
                           return myOrXorList;
                        }
                }
                return null;
        }
        
        /**
         * 
         * @param myOrXorList
         * @param orXorLists
         * @return
         */
        private boolean isXorOrListExist(ArrayList<Link> myOrXorList, ArrayList<ArrayList<Link>> orXorLists)
        {
            Comparator<Link> comprate = new Comparator<Link>() 
            {
            @Override
                public int compare(Link o1, Link o2) 
                {
                    if (o1.getDstId() > o2.getDstId())
                    {
                            return 1;
                    }
                    else if (o1.getDstId() == o2.getDstId())
                    {
                            return 0;
                    }
                    else return -1;
                }
            };
                
            Collections.sort(myOrXorList, comprate);
            for(ArrayList<Link> currList : orXorLists)
            {
                boolean flag = true;
                Collections.sort(currList, comprate);                   
                if(currList.size() != myOrXorList.size())
                {
                        continue;
                }
                
                else
                {
                        for(int i=0; i<currList.size();i++)
                        {
                                if(currList.get(i).getDstId() != myOrXorList.get(i).getDstId())
                                {
                                        flag = false;
                                }
                        }
                        if(flag == true)
                        {
                                return true;
                        }
                }           
            }
            return false;
        }
       
         /**
         * This function adds the given link to the link list of its source\destination objects
         * @param process
         * @param link
         * @param edgeId
         * @param edgeType
         * @throws IOException
         * @throws SmvDBException 
         */
        private void addLinkByEdge(SmvProcess process, Link link, long edgeId, SmvType edgeType, boolean isOrXor) throws IOException, SmvDBException 
        {               
                int index;
              
                if(edgeType == SmvType.OBJECT)
                {
                   //add link to source process
                   if(isOrXor == false)
                   {
                           process.getLinkList().add(link);     
                   }
                   
                   index = findObjectById(edgeId);
                   smvObjectList.get(index).getLinkList().add(link);
                }
                else if(edgeType == SmvType.STATE)
                {
                        //add link to source process
                         if(isOrXor == false)
                         {
                                 process.getLinkList().add(link);   
                         }
                        
                    index = findStateById(edgeId);
                    if(index != -1)
                    {
                            int objectIndex = findObjectById(statesList.get(index).getObjectId());
                            if (objectIndex != -1)
                            {
                                    smvObjectList.get(objectIndex).getLinkList().add(link);
                            }
                            else
                            {
                            	throw new SmvDBException("DiagramDatabase.addLinksToOpmElements: source object is not exist for given state!!!");
                            }
                    }
                    else
                    {
                    	throw new SmvDBException("DiagramDatabase.addLinksToOpmElements: source state is not exist!!!");
                    }
                }
        }

        /**
         * This functions runs over all the OPDs and collects all relations between objects to objects
         * and processes to processes .
         * @param currSystem The current ISystem
         * @throws IOException
         * @throws SmvDBException 
         */
        private void addRelationsToOpmElements(ISystem currSystem) throws IOException, SmvDBException
        {
            Enumeration<?> opds = currSystem.getISystemStructure().getOpds();
            
            ////running over all OPD's
            while (opds!=null && opds.hasMoreElements())
            {
                IOpd currOpd= (IOpd)opds.nextElement();
                long currOpdId = currOpd.getOpdId();
                boolean addLink = true;
                
                //running over all relations in current OPD which are FundamentalRelations
                Enumeration<?> relationElements = currSystem.getISystemStructure().getFundamentalRelationsInOpd(currOpdId);
                addRelationForCurrOpd(relationElements);
                
                //running over all relations in current OPD which are GeneralRelations
                relationElements = currSystem.getISystemStructure().getGeneralRelationsInOpd(currOpdId);
                addRelationForCurrOpd(relationElements);
                
                //running over all links in current OPD and try to find invocation link. 
                relationElements = currSystem.getISystemStructure().getLinksInOpd(currOpdId);
                while(relationElements!=null && relationElements.hasMoreElements())
                {
                   addLink = true;
                   ILinkInstance item = (ILinkInstance)relationElements.nextElement();
                   ILinkEntry itemEntry = (ILinkEntry)(item.getIEntry());
                   if (getLinkType(itemEntry.getLinkType()) == LinkType.INVOCATION)
                   {
                       IProcessInstance srcProcess = (IProcessInstance)(item.getSourceIInstance());
                       if (findProcessById(srcProcess.getIEntry().getId()) == -1)
                       {
                           continue;
                       }
                       SmvProcess source = smvProcessList.get(findProcessById(srcProcess.getIEntry().getId()));                                
                       long destId = item.getDestinationIInstance().getIEntry().getId();
                       
                       
                       for(int i=0; i<source.getChildrenIdList().size();i++)
                       {
                           if(findProcessById(source.getChildrenIdList().get(i))==-1)
                           {
                                  continue;
                           }
                           SmvProcess child = smvProcessList.get(findProcessById(source.getChildrenIdList().get(i)));
                           
                           if(processHasInvocationLinkToDst(child, destId))
                           {
                                   addLink = false;
                           }
                       }
                       if (processHasInvocationLinkToDst(source, destId))
                       {
                           addLink = false;    
                       }
                       if (addLink)
                       {
                           Link link = createLink(item, SmvType.LINK);
                          
                           int edgeIndex = findProcessById(srcProcess.getIEntry().getId());
                           smvProcessList.get(edgeIndex).getLinkList().add(link);
                      
                           edgeIndex = findProcessById(destId);
                           smvProcessList.get(edgeIndex).getLinkList().add(link);
                       }
                   }
                }
            }
        }
        
        /**
         * This function checks if the SMV process has an invocation link connecting it 
         * with the given destination id.
         * @param SMV process
         * @param destId
         * @return true if exists such link, false otherwise
         */
        private boolean processHasInvocationLinkToDst(SmvProcess process , long destId)
        {
            for(int i=0; i< process.getLinkList().size(); i++)
            {               
                if (process.getLinkList().get(i).getLinkType() == LinkType.INVOCATION)
                {
                    if(process.getLinkList().get(i).getDstId() == destId)
                    {
                            return true;
                    }
                }
                    
            }

            return false;
        }
        
        /**
         * This function runs over all the relations in the current OPD and adds them to the
         * source and destination SMV objects. 
         * @param relationElements
         * @throws IOException
         * @throws SmvDBException 
         */
        private void addRelationForCurrOpd(Enumeration<?> relationElements) throws IOException, SmvDBException 
        {
            while(relationElements!=null && relationElements.hasMoreElements())
            {
               Object item = relationElements.nextElement();

               addLinkToObject(item);
               
               continue;
            }
        }
        
        /**
         * This function adds link to an OPM object
         * @param item A given object
         * @throws IOException
         * @throws SmvDBException 
         */
        private void addLinkToObject(Object item) throws IOException, SmvDBException
        {
            Link currLink = createLink(item, SmvType.RELATION);         
            
            int srcIndex = findObjectById(currLink.getSrcId());  
            int dstIndex = findObjectById(currLink.getDstId());          
            
            if(srcIndex != -1)
            {
               if(!smvObjectList.get(srcIndex).isLinkExist(currLink))
               {
                   smvObjectList.get(srcIndex).getLinkList().add(currLink);
               }
            }
            if (dstIndex != -1)
            {
                if(!smvObjectList.get(dstIndex).isLinkExist(currLink))
                {
                   smvObjectList.get(dstIndex).getLinkList().add(currLink);
                }
            }
            
            if((srcIndex == -1)&&(dstIndex == -1))
            {   
            	throw new SmvDBException("Error in DiagramDatabase.addLinkToObject");
            }
            
            return;
            
        }
        
        /**
         * This function sets all the fields of a new link.
         * @param item
         * @param type an smvType
         * @return A link with all its fields set
         * @throws IOException
         * @throws SmvDBException 
         */
        private Link createLink(Object item, SmvType type) throws IOException, SmvDBException
        {
           Link currLink;
                
           //smvType.LINK meaning our link is ILinkInstance
           if(type == SmvType.LINK)
           {
               ILinkInstance linkItem = (ILinkInstance)item; 
               ILinkEntry linkEntry = (ILinkEntry)linkItem.getIEntry();
               String label = linkEntry.getPath();
               
               currLink = new Link (linkItem.getSourceIInstance().getIEntry().getId(),
                                    getSmvType(linkItem.getSourceIInstance().getIEntry()),
                                    linkItem.getDestinationIInstance().getIEntry().getId(),
                                    getSmvType(linkItem.getDestinationIInstance().getIEntry()), 
                                    LinkType.NOT_EXIST, label);
               
               setLinkType(currLink, ((ILinkEntry)(linkItem.getIEntry())).getLinkType());

               //boolean isXor = false;               
               //Enumeration<?> orLinks = linkItem.getOrXorSourceNeighbours(isXor); // Or
               //setOrXorRelatedLinks(currLink, orLinks, isXor);
              
               //isXor = true;
               //Enumeration<?> xorLinks = linkItem.getOrXorSourceNeighbours(isXor); // Xor
               //setOrXorRelatedLinks(currLink, xorLinks, isXor);
           }
           
           //smvType.Relation meaning our link is IRelationInstance.
           else
           {
                   IRelationInstance relationItem = (IRelationInstance)item;
                   
                   currLink = new Link (relationItem.getSourceIInstance().getIEntry().getId(),
                                        getSmvType(relationItem.getSourceIInstance().getIEntry()),
                                        relationItem.getDestinationIInstance().getIEntry().getId(),
                                        getSmvType(relationItem.getDestinationIInstance().getIEntry()), 
                                        LinkType.NOT_EXIST, "");
                   
                   setLinkType(currLink, ((IRelationEntry)(relationItem.getIEntry())).getRelationType());

           }
              
           return currLink;
           
        }
        
        /**
         * This function set all related or/xor links to a given link.
         * @param currLink a Link
         * @param orXorLinks an Enumeration
         */
        private ArrayList<Link> setOrXorRelatedLinks(Link currLink, Enumeration<?> orXorLinks, boolean isXor) 
        {
                ArrayList<Link> temp = new ArrayList<Link>();
                while (orXorLinks!= null && orXorLinks.hasMoreElements())
                   {
                           ILinkInstance relatedOrXorLink = (ILinkInstance) orXorLinks.nextElement();
                           ILinkEntry relatedOrXorLinkEntry = (ILinkEntry)relatedOrXorLink.getIEntry(); 
                           String label = relatedOrXorLinkEntry.getPath();
                           
                           Link smvRelatedOrXorLink = new Link (relatedOrXorLink.getSourceIInstance().getIEntry().getId(),
                                                                getSmvType(relatedOrXorLink.getSourceIInstance().getIEntry()),
                                                                relatedOrXorLink.getDestinationIInstance().getIEntry().getId(),
                                                                getSmvType(relatedOrXorLink.getDestinationIInstance().getIEntry()), 
                                                                currLink.getLinkType(), label);   

                           temp.add(smvRelatedOrXorLink);
                   }
                return temp;
        }
        
        /**
         * This function is used for CreateLink.
         * @param linkEdge an IEntry linkEdge
         * @return smvType of a given IEntry.
         */
        private SmvType getSmvType(IEntry linkEdge)
        {
                 if (linkEdge instanceof ObjectEntry)
                   { 
                           return SmvType.OBJECT;
                   }
                   else if(linkEdge instanceof ProcessEntry)
                   {
                           return SmvType.PROCESS; 
                   }
                   else if(linkEdge instanceof StateEntry)
                   {
                           return SmvType.STATE; 
                   }
                
                 return null;
        }
        
        /**
         * This function maps between OPM relation/link type to the proper linkType
         * @param relationType
         * @return The correct link type
         * @throws SmvDBException 
         */
        private LinkType getLinkType(int relationType) throws SmvDBException
        {
        switch (relationType)
        {
                        //object - process
                case  OpcatConstants.RESULT_LINK : 
                        return LinkType.RESULT; 
                case  OpcatConstants.CONSUMPTION_LINK : 
                        return LinkType.CONSUMPTION;
                case  OpcatConstants.EFFECT_LINK : 
                        return LinkType.EFFECT;
                case  OpcatConstants.CONDITION_LINK : 
                        return LinkType.CONDITION;
                case OpcatConstants.AGENT_LINK:
                        return LinkType.AGENT;
                case OpcatConstants.INSTRUMENT_LINK:
                        return LinkType.INSTRUMENT;
                
                        //process-process       
                case  OpcatConstants.INVOCATION_LINK : 
                        return LinkType.INVOCATION;
                
                        //object-object
                case OpcatConstants.BI_DIRECTIONAL_RELATION:
                        return LinkType.BIDIRECTIONAL;
                case OpcatConstants.UNI_DIRECTIONAL_RELATION:
                        return LinkType.UNIDIRECTIONAL;
                case OpcatConstants.AGGREGATION_RELATION:
                        return LinkType.PARTICULATION;
                case OpcatConstants.EXHIBITION_RELATION:
                        return LinkType.CHARACTERIZATION;
                case OpcatConstants.INSTANTINATION_RELATION:
                        return LinkType.INSTANTIATION;
                //ignored
                case OpcatConstants.CONSUMPTION_EVENT_LINK:
                case OpcatConstants.INSTRUMENT_EVENT_LINK:
                	return LinkType.NOT_EXIST;                	
        }
        
        throw new SmvDBException("DiagramDatabase.getLinkType: no such link!");
               
        }
        /**
         * This function set the LinkType for given Link.
         * if the LinkType doesn't exist set the type to NOT_EXIST
         * @param currLink a Link
         * @param type an int
         * @throws SmvDBException 
         */
        private void setLinkType(Link currLink, int type) throws IOException, SmvDBException 
        {       
           LinkType result = getLinkType(type);
     	   currLink.setLinkType(result);
	    }
        
        /**
         * This function add all processes that related to update rule.
         * 
         */
        private void setLableList()
        {
           	for (SmvProcess process : smvProcessList) 
        	{
        		for(Link link : process.getLinkList())
        		{
        			if (link.getLabel() != "")
        			{
        				  for (Link destLink : process.getLinkList())
        				  {
        					  if ((link.getLabel().equals(destLink.getLabel())) && (link.getDstId()== destLink.getSrcId()))    					   
        					  {
        						  State s = statesList.get(findStateById(link.getSrcId()));
        						  s.getDestStateId().put(link.getLabel(), destLink.getDstId()); 
        						  if(!(updateProcessList.contains(process))) updateProcessList.add(process);
        					  }
        					  if((link.getLabel().equals(destLink.getLabel())) && (link.getSrcId()== destLink.getDstId()))    					   
        					  {
        						  State s = statesList.get(findStateById(destLink.getSrcId()));
        						  s.getDestStateId().put(link.getLabel(), link.getDstId());
        						  if(!(updateProcessList.contains(process))) updateProcessList.add(process);
        					  }
        				  }       				 
        			}
        		  if (link.getLinkType() == LinkType.EFFECT )
   				  {
        			  if(!(updateProcessList.contains(process))) updateProcessList.add(process);
				  }
        		}				
			}
        }
        
        /**
         * This function sets all fields of a new state
         * @param myobj an IObjectInstance
         * @param currObj a SmvObject
         */
        private void setStates(IObjectInstance myobj, SmvObject currObj) 
        {
                Enumeration<?> iState = myobj.getStateInstances();
                
                while(iState != null && iState.hasMoreElements())
                {
                        State currState = new State();
                        IStateInstance myState = (IStateInstance)iState.nextElement();

                        if (((IStateEntry)myState.getIEntry()).isInitial())
                        {
                                currState.setType(stateType.sInitial);                          
                        }
                        else
                        {
                                currState.setType(stateType.sNone);
                        }
                        
                        currState.setId(myState.getIEntry().getId());
                        currState.setObjectId(currObj.getId());
                        currState.setName(myState.getIEntry().getName());
                        currObj.addState(currState);
                        
                        statesList.add(currState);
                }       
        }
        

        /**
         * This function set the time line for all the children of the given process
         * and set the total time line for the given process.
         * @param process a SmvProcess
         */
        private void setTimeLineForProcess(SmvProcess process) 
        {
                if (process.getChildrenIdList().isEmpty())
                {
                        return;
                }
                List<SmvProcess> childrenList = new ArrayList<SmvProcess>();
                
                for (int i=0; i<process.getChildrenIdList().size(); i++)
                {
                	//DEBUG
            		int y = findProcessById(process.getChildrenIdList().get(i));
            		if(y!=-1){
                		SmvProcess x = smvProcessList.get(y); 
                        childrenList.add(x);
            		}
                }
                
            Collections.sort(childrenList, new Comparator<SmvProcess>() 
                {
                @Override
                public int compare(SmvProcess o1, SmvProcess o2) 
                {
                        if (o1.getY() > o2.getY())
                                        {
                                                return 1;
                                        }
                                        else if (o1.getY() == o2.getY())
                                        {
                                                return 0;
                                        }
                                        else return -1;
                }
            });
            
              int timeLine = 1;
              childrenList.get(0).setTimeLine(1);
              for (int i=1; i<childrenList.size();i++)
              {                   
                  if (Math.abs(childrenList.get(i).getY() - childrenList.get(i-1).getY()) < 5)
                  {
                          childrenList.get(i).setTimeLine(timeLine); 
                      continue; 
                  }
                  
                          timeLine++;
                          childrenList.get(i).setTimeLine(timeLine);
              }

        }
        
        /**
         * This function set the field fatherToLeafs for each process.
         */
        private void setFatherToLeafsForProcesses()
        {
            boolean result;
            
            for(int i=0;i<smvProcessList.size();i++)
            {
                    //if the process is a leaf(without any children in any SD): FatherToLeafs = false
                    if (smvProcessList.get(i).getChildrenIdList().size() == 0)
                    {
                            continue;
                    }
                    //else check if at least 1 of the process children is a leaf
                    result = smvProcessList.get(i).checkIfFatherToLeafs(getChildrenList(smvProcessList.get(i)));
                    
                    smvProcessList.get(i).setFatherToLeafs(result);
            }       
        }
        
        /**
         * Prepare InstanceObjectList and remove instance objects from smvObjectList
         */
        private void setSmvInstanceObjectList()
        {
            ArrayList<SmvObject> objectForDelete = new ArrayList<SmvObject>();
            
            for(SmvObject currObject : smvObjectList)
            {
                        for(Link currLink : currObject.getLinkList())
                        {
                                int srcIndex = findObjectById(currLink.getSrcId());
                                int dstIndex = findObjectById(currLink.getDstId());
                                
                                if(currLink.getLinkType() == LinkType.INSTANTIATION && currLink.getSrcId() == currObject.getId())
                                {
                                        SmvObject dstObject = smvObjectList.get(dstIndex);
                                        SmvObject srcObject = smvObjectList.get(srcIndex); //the father process!
                                        
                                        objectForDelete.add(smvObjectList.get(dstIndex));
                                        
                                        smvInstanceObjectList.add(new SmvInstanceObject(dstObject, "", srcObject));   
                                }
                        }
                }
            for(SmvObject smvObject : objectForDelete)
                {
                        smvObjectList.remove(smvObject);
                }
        }

                /**
                 * @param smvInstanceObjectList the smvInstanceObjectList to set
                 */
                private void setSmvInstanceObjectList(ArrayList<SmvInstanceObject> smvInstanceObjectList) {
                        this.smvInstanceObjectList = smvInstanceObjectList;
                }

                /**
                 * 
                 * @param state
                 * @param label
                 * @return function that returns the next state - made for update rule
                 */
                public State getNextUpdate(State state, String label)
                {  
                	
                	if(!state.getDestStateId().isEmpty() && statesList!=null)
                	{
                		int index = findStateById(state.getDestStateId().get(label));
                		if (index!=-1)
                		{
                			return statesList.get(index);
                		}
                		else return null;
                	}
                	else return null;
                }
        

}
